package kline

import (
	"global/core"
	"global/libs/structs"
	"time"
	"strings"
	"github.com/ngaut/log"
	"global/util"
	"strconv"
	"github.com/shopspring/decimal"
	"global/model"
)

type PairKlineHandle struct {
	slug 			string
	symbol 			structs.Symbol
	symbolToString	string
	ctx  			*core.Context
	ticker 			*time.Ticker
	coins 			map[string]interface{}
}

func NewPairKlineHandle(
	ctx *core.Context,
	slug string,
	symbol structs.Symbol,
	coins []*model.Coin) *PairKlineHandle {

		coinMaps := make(map[string]interface{})
		for _, v := range coins {
			coinMaps[strings.ToLower(v.Symbol)] = nil
		}

	return &PairKlineHandle{
		slug: 			slug,
		symbol: 		symbol,
		ctx: 			ctx,
		symbolToString: strings.ToLower(symbol.From + "_" + symbol.To),
		ticker: 		time.NewTicker(5 * time.Second),
		coins: 			coinMaps,
	}
}

func (h *PairKlineHandle) Run() {
	for t := range h.ticker.C {
		_ = t
		h.handle()
	}
}

// 处理器
func (h *PairKlineHandle) handle() {

	var ok bool
	if ok, _ = h.checkIfPop(); !ok {
		return
	}

	detail := h.popAndGetDetail()
	if detail == nil { return }

	intervals := make(map[string]int64)
	for interval := range util.IntervalMap {
		t := util.FormatTimeToInterval(detail.DateTime, interval)
		if t == 0 { continue }
		intervals[interval] = t
	}

	save := make(map[string]structs.SaveKlineOption)
	results := h.getLatestAnalysisKlineDetail(intervals)
	for k, v := range intervals {
		tmp := make([]string, 6)
		var isNew bool
		res, ok := results[k]
		if ok {
			i, _ := strconv.Atoi(res[0])
			if int64(i) != v {
				isNew = true
			}
		} else {
			isNew = true
		}

		if isNew {
			tmp[0] = strconv.Itoa(int(v))
			tmp[1] = detail.Open
			tmp[2] = detail.High
			tmp[3] = detail.Low
			tmp[4] = detail.Close
			tmp[5] = detail.Volume
		} else {
			tmp[0] = strconv.Itoa(int(v))
			tmp[1] = h.getGreater(res[1], detail.Open)
			tmp[2] = h.getGreater(res[2], detail.Open)
			tmp[3] = h.getGreater(res[3], detail.Open)
			tmp[4] = h.getGreater(res[4], detail.Open)
			tmp[5] = h.getSum(res[5], detail.Open)
		}

		save[k] = structs.SaveKlineOption{
			IsNew: 	isNew,
			Data: 	tmp,
		}
	}
	h.save(save)

	// 添加到队列
	if _, ok := h.coins[strings.ToLower(h.symbol.From)]; ok {
		q := &structs.CoinKlineQueue{
			ExchangeName: 		h.slug,
			CoinName: 			h.symbol.From,
			Timestamp: 			detail.DateTime,
			SymbolStr: 			h.symbolToString,
		}
		// save to queue
		h.saveToQueue(q, h.symbol.From)
	}
}

// 保存到队列
func (h *PairKlineHandle) saveToQueue(q *structs.CoinKlineQueue, coinName string) {
	conn := h.ctx.GetBaseRedis().Get()
	defer conn.Close()
	err := core.SaveAnalysisKlineCoinToQueue(conn, coinName, q)
	if err != nil {
		log.Errorf("Error: %s", err)
	}
}

// 存储
func (h *PairKlineHandle) save(datas map[string]structs.SaveKlineOption) {
	conn := h.ctx.GetBaseRedis().Get()
	defer conn.Close()
	err := core.SaveAnalysisKlineDetail(conn, h.slug, h.symbolToString, datas)
	if err != nil {
		log.Errorf("Error: %s", err)
	}
}

func (h *PairKlineHandle) getSum(o, n string) string {
	oldP, _ := decimal.NewFromString(o)
	newP, _ := decimal.NewFromString(n)
	return oldP.Add(newP).String()
}

// 比较得到较大值
func (h *PairKlineHandle) getGreater(oldP, newP string) string {
	o, _ := decimal.NewFromString(oldP)
	n, _ := decimal.NewFromString(newP)
	if o.GreaterThan(n) {
		return oldP
	}
	return newP
}

func (h *PairKlineHandle) getLatestAnalysisKlineDetail(
	intervals map[string]int64) map[string][]string {
	conn := h.ctx.GetBaseRedis().Get()
	defer conn.Close()
	return core.GetAnalysisKlinePairLastIntervalTime(conn, h.slug, h.symbolToString, intervals)
}

// 获取需要的数据
func (h *PairKlineHandle) popAndGetDetail() *structs.SpiderKline {
	conn := h.ctx.GetBaseRedis().Get()
	defer conn.Close()
	spiderKline, err := core.PopTimeAndGetSpiderKlineDetail(conn, h.slug, h.symbolToString, "1min")
	if err != nil {
		log.Errorf("Error: %s", err)
		return nil
	}
	return spiderKline
}

// 检查是否可以从List中获取数据
// 最后一条数据不能获取
func (h *PairKlineHandle) checkIfPop() (bool, int64) {
	conn := h.ctx.GetBaseRedis().Get()
	defer conn.Close()

	last, err := core.GetSpiderKlineLastTime(conn, h.slug, "1min", h.symbolToString)
	if err != nil {
		log.Errorf("Error: %s", err)
		return false, 0
	}

	first, err := core.GetSpiderKlineFirstTime(conn, h.slug, "1min", h.symbolToString)
	if err != nil {
		log.Errorf("Error: %s", err)
		return false, 0
	}

	if first < int64(last) {
		return true, first
	}

	return false, 0
}